package ar.edu.itba.pod.legajo45129.simul.communication;

import java.io.Serializable;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.apache.log4j.Logger;

import ar.edu.itba.pod.legajo45129.Configuration;
import ar.edu.itba.pod.legajo45129.MessageProcessor;
import ar.edu.itba.pod.legajo45129.simul.communication.factory.MessageFactory;
import ar.edu.itba.pod.simul.communication.AgentDescriptor;
import ar.edu.itba.pod.simul.communication.ConnectionManager;
import ar.edu.itba.pod.simul.communication.NodeAgentLoad;
import ar.edu.itba.pod.simul.communication.SimulationCommunication;
import ar.edu.itba.pod.simul.simulation.Agent;

import com.google.common.base.Preconditions;



public class SimulationCommunicationImpl implements SimulationCommunication, Serializable {
	/**
	 * 
	 */
	private static final long serialVersionUID = 7553324818542463899L;
//	private Queue<AgentDescriptor> queue;
	private NodeAgentLoad nodeAgentLoad;
	private ConnectionManagerImpl connectionManager;
	private static Logger logger = Logger.getLogger(SimulationCommunicationImpl.class);
	
	public SimulationCommunicationImpl(ConnectionManager connectionManager) throws RemoteException {
		UnicastRemoteObject.exportObject(this, 0);
		this.connectionManager = (ConnectionManagerImpl) connectionManager;
//		this.queue = new LinkedBlockingQueue<AgentDescriptor>();
		this.nodeAgentLoad = new NodeAgentLoad(this.connectionManager.getNodeId().toString(), 
				this.connectionManager.getSimulationManager().simulation().getAgents().size());
	}

	@Override
	public NodeAgentLoad getMinimumNodeKnownLoad() throws RemoteException {
		NodeAgentLoad ret;
		
		connectionManager.getGroupCommunication().broadcast(MessageFactory.NodeAgentsLoadRequest(connectionManager.getNodeId().toString()));
		
		try {
			Thread.sleep(Configuration.BROADCAST_WAIT);
		} catch (InterruptedException e) {
			logger.error("error waiting for broadcast message: " + e.getMessage());
		}
		
		
		ret = MessageProcessor.instance.getMinimumNodeLoad();
		MessageProcessor.instance.resetLoads();
		
		return ret;
		
		
	}

	@Override
	public synchronized Collection<AgentDescriptor> migrateAgents(int numberOfAgents) {
		Preconditions.checkArgument(numberOfAgents > 0);
//		Preconditions.checkNotNull(queue);
//		Preconditions.checkState(queue.size() >= numberOfAgents);
		
		logger.debug("migrate agents: " + numberOfAgents);
		
		List<AgentDescriptor> ret = new ArrayList<AgentDescriptor>();
		List<Agent> listAgents = connectionManager.getSimulationManager().simulation().getAgents();
		synchronized (listAgents) {
			Preconditions.checkState(listAgents.size() >= numberOfAgents);
			
			int count = 0;
			for (Agent agent: listAgents) {
				if (count == numberOfAgents) {
					break;
				}
				
				count++;
				
				connectionManager.getSimulationManager().simulation().removeAgent(agent);
				
//				try {
//					agent.join();
//				} catch (InterruptedException e) {
//					// TODO Auto-generated catch block
//					e.printStackTrace();
//				}
				
				ret.add(agent.getAgentDescriptor());
			}
						
			nodeAgentLoad.setNumberOfAgents(listAgents.size()-numberOfAgents);

			return ret;
		}
		
	}
	
	@Override
	public void nodeLoadModified(NodeAgentLoad newLoad) throws RemoteException {
		Preconditions.checkNotNull(newLoad);
		nodeAgentLoad = new NodeAgentLoad(newLoad.getNodeId(), newLoad.getNumberOfAgents());
	}

	@Override
	public void startAgent(AgentDescriptor descriptor) throws RemoteException {
		Preconditions.checkNotNull(descriptor);
//		queue.add(descriptor);
		Agent agent = descriptor.build();
		connectionManager.getSimulationManager().simulation().addAgent(agent);
		agent.start();
		nodeAgentLoad.setNumberOfAgents(getAgentsCount());
		
	}
	
	public int getAgentsCount() {
		return connectionManager.getSimulationManager().simulation().getAgents().size();
	}
	
	public NodeAgentLoad getNodeAgentLoad() {
		return nodeAgentLoad;
	}

}
